package org.lex.expression.tree;

import java.io.PrintStream;
import java.util.Arrays;
import java.util.Enumeration;

import javax.swing.tree.TreeNode;

import org.lex.expression.Element;
import org.lex.expression.Operand;
import org.lex.expression.Operator;

public class Node implements TreeNode {
	private static final char INDENT_CHAR = ' ';
	private static final Node[] EMPTY_ARRAY = new Node[0];

	private Element element = null;
	private Node parent = null;
	private Node[] children = EMPTY_ARRAY;

	public static Node newChildNode(Element e) {
		return new Node(e);
	}

	public static Node newParentNode(Element e, Node... children) {
		Node parent = new Node(e);
		parent.children = Arrays.copyOf(children, children.length);
		for (Node child : children)
			child.setParent(parent);
		return parent;
	}

	private Node(Element node) {
		this.element = node;
	}

	// ==========================
	public Number getValue() {
		if (true == this.element.isOperand())
			return ((Operand) element).getValue();
		else {
			return ((Operator) element).operate(children[0].getValue(), children[1].getValue());
		}
	}

	// ===========================
	public Enumeration children() {
		return new Enumeration() {
			private int index = 0;

			public boolean hasMoreElements() {
				return index < children.length;
			}

			public Object nextElement() {
				return children[index];
			}
		};
	}

	public boolean getAllowsChildren() {
		return this.element.isOperator();
	}

	public TreeNode getChildAt(int childIndex) {
		return this.children[childIndex];
	}

	public int getChildCount() {
		return this.children.length;
	}

	public int getIndex(TreeNode node) {
		if (node instanceof Node) {
			Node n = (Node) node;
			for (int i = 0; i < this.children.length; i++)
				if (n == this.children[i])
					return i;
		}
		return 0;
	}

	public void setParent(Node parent) {
		this.parent = parent;
	}

	public TreeNode getParent() {
		return parent;
	}

	public boolean isLeaf() {
		return element.isOperand();
	}

	public String toString() {
		if (null == this.element)
			return "null";
		if (true == element.isOperand())
			return element.getImage();

		return element.getImage() + " [" + getValue().toString() + "]";
	}

	public void print(PrintStream out, int indent) {
		this.print0(out, 0, indent);
	}

	private void print0(PrintStream out, int indent, int step) {
		printIndent(out, indent);
		if (null == this.element)
			out.println("null");
		else if (true == this.element.isOperand()) {
			out.println(element.getImage());
		} else {
			// this is an operator node
			out.println(element.getImage());
			for (Node node : children)
				node.print0(out, indent + step, step);
		}
	}

	private void printIndent(PrintStream out, int n) {
		for (int i = 0; i < n; i++)
			out.print(INDENT_CHAR);
	}
}
